using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;

namespace ZFramework.Runtime
{
    public partial class ItemMgr : FrameworkManagerBase
    {
        private Dictionary<int, ItemInfo> _itemInfos;
        private Dictionary<string, ItemGroup> _itemGroups; //分组
        private Dictionary<int, int> _itemsBeLoading; //加载中的
        private HashSet<int> _items2ReleaseOnLoad; //加载完成后就卸载的
        private LoadAssetCallbacks _loadAssetCallbacks;
        private Queue<ItemInfo> _recycleQueue;
        private int _serialId; //真正加载一次资源，这个值才会+1
        private IEventManager _eventMgr;
        private IResourceMgr _resourceMgr;
        private ObjectPoolMgr _objectPoolMgr;

        protected override void Awake()
        {
            base.Awake();
            _serialId = 0;
            _itemInfos = new Dictionary<int, ItemInfo>();
            _itemGroups = new Dictionary<string, ItemGroup>();
            _itemsBeLoading = new Dictionary<int, int>();
            _items2ReleaseOnLoad = new HashSet<int>();
            _loadAssetCallbacks = new LoadAssetCallbacks(LoadAssetSuccessCallback, LoadAssetFailureCallback);
            _recycleQueue = new Queue<ItemInfo>();
        }

        void Start()
        {
            _eventMgr = FrameworkManagerManager.GetManager<EventManager>();
            if (_eventMgr == null)
            {
                Log.Error("EventMgr is invalid.");
                return;
            }
            _resourceMgr = FrameworkManagerManager.GetManager<YooResourceMgr>();
            if (_resourceMgr == null)
            {
                Log.Error("YooResourceMgr is invalid.");
                return;
            }
            _objectPoolMgr = FrameworkManagerManager.GetManager<ObjectPoolMgr>();
            if (_objectPoolMgr == null)
            {
                Log.Error("ObjectPoolMgr is invalid.");
                return;
            }
        }

        public override void Shutdown()
        {
        }

        public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {
            while (_recycleQueue.Count > 0)
            {
                ItemInfo itemInfo = _recycleQueue.Dequeue();
                Item item = itemInfo.Item;
                ItemGroup itemGroup = (ItemGroup) item.ItemGroup;
                if (itemGroup == null)
                {
                    throw new Exception("item group is invalid");
                }

                itemInfo.Status = ItemStatus.WillRecycle;
                item.OnRecycle();
                itemInfo.Status = ItemStatus.Recycled;
                itemGroup.UnSpawnItem(item);
                ReferencePool.Release(itemInfo);
            }

            foreach (var item in _itemGroups)
            {
                item.Value.OnUpdate(elapseSeconds, realElapseSeconds);
            }
        }

        public bool AddItemGroup(string itemGroupName)
        {
            if (string.IsNullOrEmpty(itemGroupName))
            {
                return false;
            }

            if (HasItemGroup(itemGroupName))
            {
                return false;
            }

            var itemGroupGo = new GameObject(itemGroupName);
            itemGroupGo.transform.SetParent(transform);
            var itemGroup = itemGroupGo.AddComponent<ItemGroup>();
            itemGroup.Init(itemGroupName, 60, 20, 60, 1, _objectPoolMgr);
            _itemGroups.Add(itemGroupName, itemGroup);

            return true;
        }

        public void ShowItem(int itemId, Type itemLogicType, string itemAssetName, string itemGroupName,
            int priority = 0, object userData = null)
        {
            if (HasItem(itemId))
            {
                Log.Error("重复show item  id=={0}", itemId);
                return;
            }

            if (IsLoadingItem(itemId))
            {
                Log.Error("show item  id=={0}正在加载中", itemId);
                return;
            }

            ItemGroup itemGroup = (ItemGroup) GetItemGroup(itemGroupName);
            if (itemGroup == null)
            {
                Log.Error("ShowItem  id=={0} 不存在的 itemGroup=={1}", itemId, itemGroupName);
                return;
            }

            var showItemInfo = ShowItemInfo.Create(itemLogicType, userData);
            var itemInstanceObject = itemGroup.SpawnItemInstanceObject(itemAssetName);
            if (itemInstanceObject == null)
            {
                var serialId = ++_serialId;
                _itemsBeLoading.Add(itemId, serialId);
                _resourceMgr.LoadAssetAsync<GameObject>(itemAssetName, _loadAssetCallbacks,
                    InternalShowItemInfo.Create(serialId, itemId, itemGroup, showItemInfo));
                return;
            }

            InternalShowItem(itemId, itemAssetName, itemGroup, (Object)itemInstanceObject.Instance, false, 0f, showItemInfo);
        }

        public void HideItem(int itemId, object userData = null)
        {
            if (IsLoadingItem(itemId))
            {
                _items2ReleaseOnLoad.Add(_itemsBeLoading[itemId]);
                _itemsBeLoading.Remove(itemId);
                return;
            }

            ItemInfo itemInfo = _GetItemInfo(itemId);
            if (itemInfo == null)
            {
                return;
            }

            InternalHideItem(itemInfo, userData);
        }

        public bool HasItem(int itemId)
        {
            return _itemInfos.ContainsKey(itemId);
        }

        public bool IsLoadingItem(int itemId)
        {
            return _itemsBeLoading.ContainsKey(itemId);
        }

        public ItemGroup GetItemGroup(string itemGroupName)
        {
            if (string.IsNullOrEmpty(itemGroupName))
            {
                return null;
            }

            if (_itemGroups.TryGetValue(itemGroupName, out var itemGroup))
            {
                return itemGroup;
            }

            return null;
        }

        private ItemInfo _GetItemInfo(int itemId)
        {
            if (_itemInfos.TryGetValue(itemId, out var itemInfo))
            {
                return itemInfo;
            }

            return null;
        }

        private bool HasItemGroup(string itemGroupName)
        {
            if (_itemGroups.ContainsKey(itemGroupName))
            {
                return true;
            }

            return false;
        }

        private void LoadAssetSuccessCallback(string itemAssetName, Object itemAsset, float duration, object userData)
        {
            InternalShowItemInfo internalShowItemInfo = (InternalShowItemInfo) userData;
            if (internalShowItemInfo == null)
            {
                return;
            }

            if (_items2ReleaseOnLoad.Contains(internalShowItemInfo.SerialId))
            {
                _items2ReleaseOnLoad.Remove(internalShowItemInfo.SerialId);
                ReferencePool.Release(internalShowItemInfo);
                ReleaseItem(itemAssetName, null);
                return;
            }

            _itemsBeLoading.Remove(internalShowItemInfo.ItemId);
            var itemInstance = InstantiateItem(itemAsset);
            ItemInstanceObject itemInstanceObject =
                ItemInstanceObject.Create(itemAssetName, itemAsset, itemInstance, _resourceMgr);
            internalShowItemInfo.ItemGroup.RegisterItemInstanceObject(itemInstanceObject, true);

            InternalShowItem(internalShowItemInfo.ItemId, itemAssetName, internalShowItemInfo.ItemGroup, itemInstance,
                true, duration, internalShowItemInfo.UserData);
            ReferencePool.Release(internalShowItemInfo);
        }

        private void LoadAssetFailureCallback(string assetName, string errorMessage, object userData)
        {
            InternalShowItemInfo internalShowItemInfo = (InternalShowItemInfo) userData;
            if (internalShowItemInfo == null)
            {
                return;
            }

            if (_items2ReleaseOnLoad.Contains(internalShowItemInfo.SerialId))
            {
                _items2ReleaseOnLoad.Remove(internalShowItemInfo.SerialId);
                return;
            }

            _itemsBeLoading.Remove(internalShowItemInfo.ItemId);
            _eventMgr.Fire(this,
                ShowItemFailureEventArgs.Create(internalShowItemInfo.ItemId, assetName,
                    internalShowItemInfo.ItemGroup.Name, "加载item资源失败", internalShowItemInfo.UserData));
        }

        private void InternalShowItem(int itemId, string itemAssetName, ItemGroup itemGroup, Object itemInstance,
            bool isNewInstance, float duration, ShowItemInfo userData)
        {
            Item item = CreateItem(itemInstance, itemGroup, userData);
            if (item == null)
            {
                _eventMgr.Fire(this,
                    ShowItemFailureEventArgs.Create(itemId, itemAssetName, itemGroup.Name, "创建item失败", userData));
                return;
            }

            ItemInfo itemInfo = ItemInfo.Create(item);
            _itemInfos.Add(itemId, itemInfo);
            itemInfo.Status = ItemStatus.WillInit;
            item.OnInit(itemId, itemAssetName, itemGroup, isNewInstance, userData);
            itemInfo.Status = ItemStatus.Inited;
            itemGroup.AddItem(item);
            itemInfo.Status = ItemStatus.WillShow;
            item.OnShow(userData);
            itemInfo.Status = ItemStatus.Showed;

            _eventMgr.Fire(this, ShowItemSuccessEventArgs.Create(item, duration, userData));
        }

        private void InternalHideItem(ItemInfo itemInfo, object userData)
        {
            if (itemInfo.Status == ItemStatus.Hidden)
            {
                return;
            }

            Item item = itemInfo.Item;
            itemInfo.Status = ItemStatus.WillHide;
            item.OnHide(userData);
            itemInfo.Status = ItemStatus.Hidden;

            ItemGroup itemGroup = (ItemGroup) item.ItemGroup;
            if (itemGroup == null)
            {
                return;
            }

            itemGroup.RemoveItem(item);
            if (!_itemInfos.Remove(item.Id))
            {
                return;
            }

            _eventMgr.Fire(this, HideItemCompleteEventArgs.Create(item.Id, item.AssetPath, item.ItemGroup, userData));

            _recycleQueue.Enqueue(itemInfo);
        }

        private Object InstantiateItem(Object itemAsset)
        {
            return Instantiate(itemAsset);
        }

        private Item CreateItem(Object itemInstance, ItemGroup itemGroup, object userData)
        {
            GameObject instance = itemInstance as GameObject;
            if (instance == null)
            {
                Log.Error("Item instance is invalid.");
                return null;
            }

            Transform tran = instance.transform;
            tran.SetParent(itemGroup.Root.transform);
            var item = instance.GetComponent<Item>();
            if (item == null)
            {
                item = instance.AddComponent<Item>();
            }

            return item;
        }

        private void ReleaseItem(string assetPath, Object itemInstance)
        {
            _resourceMgr.UnloadAsset(assetPath);
            if (itemInstance != null)
            {
                Destroy(itemInstance);
            }
        }

    }
}
